home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / perl5 / FreezeThaw.pm < prev    next >
Text File  |  2009-02-10  |  25KB  |  871 lines

  1. =head1 NAME
  2.  
  3. FreezeThaw - converting Perl structures to strings and back.
  4.  
  5. =head1 SYNOPSIS
  6.  
  7.   use FreezeThaw qw(freeze thaw cmpStr safeFreeze cmpStrHard);
  8.   $string = freeze $data1, $data2, $data3;
  9.   ...
  10.   ($olddata1, $olddata2, $olddata3) = thaw $string;
  11.   if (cmpStr($olddata2,$data2) == 0) {print "OK!"}
  12.  
  13. =head1 DESCRIPTION
  14.  
  15. Converts data to/from stringified form, appropriate for
  16. saving-to/reading-from permanent storage.
  17.  
  18. Deals with objects, circular lists, repeated appearence of the same
  19. refence. Does not deal with overloaded I<stringify> operator yet.
  20.  
  21. =head1 EXPORT
  22.  
  23. =over 12
  24.  
  25. =item Default
  26.  
  27. None.
  28.  
  29. =item Exportable
  30.  
  31. C<freeze thaw cmpStr cmpStrHard safeFreeze>.
  32.  
  33. =back
  34.  
  35. =head1 User API
  36.  
  37. =over 12
  38.  
  39. =item C<cmpStr>
  40.  
  41. analogue of C<cmp> for data. Takes two arguments and compares them as
  42. separate entities.
  43.  
  44. =item C<cmpStrHard>
  45.  
  46. analogue of C<cmp> for data. Takes two arguments and compares them
  47. considered as a group.
  48.  
  49. =item C<freeze>
  50.  
  51. returns a string that encupsulates its arguments (considered as a
  52. group). C<thaw>ing this string leads to a fatal error if arguments to
  53. C<freeze> contained references to C<GLOB>s and C<CODE>s.
  54.  
  55. =item C<safeFreeze>
  56.  
  57. returns a string that encupsulates its arguments (considered as a
  58. group). The result is C<thaw>able in the same process. C<thaw>ing the
  59. result in a different process should result in a fatal error if
  60. arguments to C<safeFreeze> contained references to C<GLOB>s and
  61. C<CODE>s.
  62.  
  63. =item C<thaw>
  64.  
  65. takes one string argument and returns an array. The elements of the
  66. array are "equivalent" to arguments of the C<freeze> command that
  67. created the string. Can result in a fatal error (see above).
  68.  
  69. =back
  70.  
  71. =head1 Developer API
  72.  
  73. C<FreezeThaw> C<freeze>s and C<thaw>s data blessed in some package by
  74. calling methods C<Freeze> and C<Thaw> in the package. The fallback
  75. methods are provided by the C<FreezeThaw> itself. The fallback
  76. C<Freeze> freezes the "content" of blessed object (from Perl point of
  77. view). The fallback C<Thaw> blesses the C<thaw>ed data back into the package.
  78.  
  79. So the package needs to define its own methods only if the fallback
  80. methods will fail (for example, for a lot of data the "content" of an
  81. object is an address of some B<C> data). The methods are called like
  82.  
  83.   $newcooky = $obj->Freeze($cooky);
  84.   $obj = Package->Thaw($content,$cooky);
  85.  
  86. To save and restore the data the following method are applicable:
  87.  
  88.   $cooky->FreezeScalar($data,$ignorePackage,$noduplicate);
  89.  
  90. during Freeze()ing, and
  91.  
  92.   $data = $cooky->ThawScalar;
  93.  
  94. Two optional arguments $ignorePackage and $noduplicate regulate
  95. whether the freezing should not call the methods even if $data is a
  96. reference to a blessed object, and whether the data should not be
  97. marked as seen already even if it was seen before. The default methods
  98.  
  99.   sub UNIVERSAL::Freeze {
  100.     my ($obj, $cooky) = (shift, shift);
  101.     $cooky->FreezeScalar($obj,1,1);
  102.   }
  103.  
  104.   sub UNIVERSAL::Thaw {
  105.     my ($package, $cooky) = (shift, shift);
  106.     my $obj = $cooky->ThawScalar;
  107.     bless $obj, $package;
  108.   }
  109.  
  110. call the C<FreezeScalar> method of the $cooky since the freezing
  111. engine will see the data the second time during this call. Indeed, it
  112. is the freezing engine who calls UNIVERSAL::Freeze(), and it calls it
  113. because it needs to freeze $obj. The above call to
  114. $cooky->FreezeScalar() handles the same data back to engine, but
  115. because flags are different, the code does not cycle.
  116.  
  117. Freezing and thawing $cooky also allows the following additional methods:
  118.  
  119.   $cooky->isSafe;
  120.  
  121. to find out whether the current freeze was initiated by C<freeze> or
  122. C<safeFreeze> command. Analogous method for thaw $cooky returns
  123. whether the current thaw operation is considered safe (i.e., either
  124. does not contain cached elsewhere data, or comes from the same
  125. application). You can use
  126.  
  127.   $cooky->makeSafe;
  128.  
  129. to prohibit cached data for the duration of the rest of freezing or
  130. thawing of current object.
  131.  
  132. Two methods
  133.  
  134.   $value = $cooky->repeatedOK;
  135.   $cooky->noRepeated;        # Now repeated are prohibited
  136.  
  137. allow to find out/change the current setting for allowing repeated
  138. references.
  139.  
  140. If you want to flush the cache of saved objects you can use
  141.  
  142.   FreezeThaw->flushCache;
  143.  
  144. this can invalidate some frozen string, so that thawing them will
  145. result in fatal error.
  146.  
  147. =head2 Instantiating
  148.  
  149. Sometimes, when an object from a package is recreated in presense of
  150. repeated references, it is not safe to recreate the internal structure
  151. of an object in one step. In such a situation recreation of an object
  152. is carried out in two steps: in the first the object is C<allocate>d,
  153. in the second it is C<instantiate>d.
  154.  
  155. The restriction is that during the I<allocation> step you cannot use any
  156. reference to any Perl object that can be referenced from any other
  157. place. This restriction is applied since that object may not exist yet.
  158.  
  159. Correspondingly, during I<instantiation> step the previosly I<allocated>
  160. object should be C<filled>, i.e., it can be changed in any way such
  161. that the references to this object remain valid.
  162.  
  163. The methods are called like this:
  164.  
  165.   $pre_object_ref = Package->Allocate($pre_pre_object_ref);
  166.     # Returns reference
  167.   Package->Instantiate($pre_object_ref,$cooky);
  168.     # Converts into reference to blessed object
  169.  
  170. The reverse operations are
  171.  
  172.   $object_ref->FreezeEmpty($cooky);
  173.   $object_ref->FreezeInstance($cooky);
  174.  
  175. during these calls object can C<freezeScalar> some information (in a
  176. usual way) that will be used during C<Allocate> and C<Instantiate>
  177. calls (via C<thawScalar>). Note that the return value of
  178. C<FreezeEmpty> is cached during the phase of creation of uninialized
  179. objects. This B<must> be used like this: the return value is the
  180. reference to the created object, so it is not destructed until other
  181. objects are created, thus the frozen values of the different objects
  182. will not share the same references. Example of bad result:
  183.  
  184.   $o1->FreezeEmpty($cooky)
  185.  
  186. freezes C<{}>, and C<$o2-E<gt>FreezeEmpty($cooky)> makes the same. Now
  187. nobody guaranties that that these two copies of C<{}> are different,
  188. unless a reference to the first one is preserved during the call to
  189. C<$o2-E<gt>FreezeEmpty($cooky)>. If C<$o1-E<gt>FreezeEmpty($cooky)>
  190. returns the value of C<{}> it uses, it will be preserved by the
  191. engine.
  192.  
  193. The helper function C<FreezeThaw::copyContents> is provided for
  194. simplification of instantiation. The syntax is
  195.  
  196.   FreezeThaw::copyContents $to, $from;
  197.  
  198. The function copies contents the object $from point to into what the
  199. object $to points to (including package for blessed references). Both
  200. arguments should be references.
  201.  
  202. The default methods are provided. They do the following:
  203.  
  204. =over 12
  205.  
  206. =item C<FreezeEmpty>
  207.  
  208. Freezes an I<empty> object of underlying type.
  209.  
  210. =item C<FreezeInstance>
  211.  
  212. Calls C<Freeze>.
  213.  
  214. =item C<Allocate>
  215.  
  216. Thaws what was frozen by C<FreezeEmpty>.
  217.  
  218. =item C<Instantiate>
  219.  
  220. Thaws what was frozen by C<FreezeInstance>, uses C<copyContents> to
  221. transfer this to the $pre_object.
  222.  
  223. =back
  224.  
  225. =head1 BUGS and LIMITATIONS
  226.  
  227. A lot of objects are blessed in some obscure packages by XSUB
  228. typemaps. It is not clear how to (automatically) prevent the
  229. C<UNIVERSAL> methods to be called for objects in these packages.
  230.  
  231. The objects which can survive freeze()/thaw() cycle must also survive a
  232. change of a "member" to an equal member.  Say, after
  233.  
  234.   $a = [a => 3];
  235.   $a->{b} = \ $a->{a};
  236.  
  237. $a satisfies
  238.  
  239.   $a->{b} == \ $a->{a}
  240.  
  241. This property will be broken by freeze()/thaw(), but it is also broken by
  242.  
  243.   $a->{a} = delete $a->{a};
  244.  
  245. =cut
  246.  
  247. require 5.002;            # defined ref stuff...
  248.  
  249. # Different line noise chars:
  250. #
  251. # $567|            next 567 chars form a scalar
  252. #
  253. # @34|            next 34 scalars form an array
  254. #
  255. # %34|            next 34 scalars form a hash
  256. #
  257. # ?            next scalar is a safe-stamp at beginning
  258. #
  259. # ?            next scalar is a stringified data
  260. #
  261. # !  repeated array follows (after a scalar denoting array $#),
  262. # (possibly?) followed by instantiation array. At beginning
  263. #
  264. # <45|            ordinal of element in repeated array
  265. #
  266. # *            stringified glob follows
  267. #
  268. # &            stringified coderef follows
  269. #
  270. # \\            stringified defererenced data follows
  271. #
  272. # /            stringified REx follows
  273. #
  274. # >            stringified package name follows, then frozen data
  275. #
  276. # {            stringified package name follows, then allocation data
  277. #
  278. # }            stringified package name follows, then instantiation data
  279. #
  280. # _            frozen form of undef
  281.  
  282.  
  283. package FreezeThaw;
  284.  
  285. use Exporter;
  286.  
  287. @ISA = qw(Exporter);
  288. $VERSION = '0.45';
  289. @EXPORT_OK = qw(freeze thaw cmpStr cmpStrHard safeFreeze);
  290.  
  291. use strict;
  292. use Carp;
  293.  
  294. my $lock = (reverse time) ^ $$ ^ \&freezeString; # To distingush processes
  295.  
  296. use vars qw( @multiple
  297.          %seen_packages
  298.          $seen_packages
  299.          %seen_packages
  300.          %count
  301.          %address
  302.          $string
  303.          $unsafe
  304.          $noCache
  305.          $cooky
  306.          $secondpass
  307.        ),            # Localized in freeze()
  308.     qw( $norepeated ),    # Localized in freezeScalar()
  309.     qw( $uninitOK ),    # Localized in thawScalar()
  310.     qw( @uninit ),        # Localized in thaw()
  311.     qw($safe);        # Localized in safeFreeze()
  312. my (%saved);
  313.  
  314. my %Empty = ( ARRAY   => sub {[]}, HASH => sub {{}},
  315.           SCALAR  => sub {my $undef; \$undef},
  316.           REF     => sub {my $undef; \$undef},
  317.           CODE    => 1,        # 1 means atomic
  318.           GLOB    => 1,
  319.           Regexp  => 0,
  320.      );
  321.  
  322. # This should better be done via pos() and \G, but apparently \G is not
  323. # optimized (bug in the REx optimizer???)
  324. BEGIN {
  325.   my $pointer_size   = length pack 'p', 0;
  326.   #my $max_dig0 = 3*$pointer_size;    # 8bits take less than 3 decimals
  327.     # Now calculate the exact value:
  328.   #my $max_pointer = sprintf "%.${max_dig0}g", 0x100**$pointer_size;
  329.   my $max_pointer = sprintf "%.0f", 0x100**$pointer_size;
  330.   die "Panic" if $max_pointer =~ /\D/;
  331.   my $max_pointer_l = length $max_pointer;
  332.   warn "Max pointer_l=$max_pointer_l" if $ENV{FREEZE_THAW_WARN};
  333.   eval "sub max_strlen_l () {$max_pointer_l}; 1" or die;
  334. }
  335.  
  336. sub flushCache {$lock ^= rand; undef %saved;}
  337.  
  338. sub getref ($) {
  339.   my $ref = ref $_[0];
  340.   return $ref if not $ref or defined $Empty{$ref}; # Optimization _and_ Regexp
  341.   my $str;
  342.   if (defined &overload::StrVal) {
  343.     $str = overload::StrVal($_[0]);
  344.   } else {
  345.     $str = "$_[0]";
  346.   }
  347.   $ref = $1 if $str =~ /=(\w+)/;
  348.   $ref;
  349. }
  350.  
  351. sub freezeString {$string .= "\$" . length($_[0]) . '|' . $_[0]}
  352.  
  353. sub freezeNumber {$string .= $_[0] . '|'}
  354.  
  355. sub freezeREx {$string .= '/' . length($_[0]) . '|' . $_[0]}
  356.  
  357. sub thawString {    # Returns list: a string and offset of rest
  358.   substr($string, $_[0], 2+max_strlen_l) =~ /^\$(\d+)\|/
  359.     or confess "Wrong format of frozen string: " . substr($string, $_[0]);
  360.   length($string) - $_[0] > length($1) + 1 + $1
  361.     or confess "Frozen string too short: `" .
  362.       substr($string, $_[0]) . "', expect " . (length($1) + 2 + $1);
  363.   (substr($string, $_[0] + length($1) + 2, $1), $_[0] + length($1) + 2 + $1);
  364. }
  365.  
  366. sub thawNumber {    # Returns list: a number and offset of rest
  367.   substr($string, $_[0], 1+max_strlen_l) =~ /^(\d+)\|/
  368.     or confess "Wrong format of frozen string: " . substr($string, $_[0]);
  369.   ($1, $_[0] + length($1) + 1);
  370. }
  371.  
  372. sub _2rex ($);
  373. if (eval '"Regexp" eq ref qr/1/') {
  374.   eval 'sub _2rex ($) {my $r = shift; qr/$r/} 1' or die;
  375. } else {
  376.   eval 'sub _2rex ($) { shift } 1' or die;
  377. }
  378.  
  379. sub thawREx {    # Returns list: a REx and offset of rest
  380.   substr($string, $_[0], 2+max_strlen_l) =~ m,^/(\d+)\|,
  381.     or confess "Wrong format of frozen REx: " . substr($string, $_[0]);
  382.   length($string) - $_[0] > length($1) + 1 + $1
  383.     or confess "Frozen string too short: `" .
  384.       substr($string, $_[0]) . "', expect " . (length($1) + 2 + $1);
  385.   (_2rex substr($string, $_[0] + length($1) + 2, $1),
  386.    $_[0] + length($1) + 2 + $1);
  387. }
  388.  
  389. sub freezeArray {
  390.   $string .= '@' . @{$_[0]} . '|';
  391.   for (@{$_[0]}) {
  392.     freezeScalar($_);
  393.   }
  394. }
  395.  
  396. sub thawArray {
  397.   substr($string, $_[0], 2+max_strlen_l) =~ /^[\@%](\d+)\|/ # % To make it possible thaw hashes
  398.     or confess "Wrong format of frozen array: \n$_[0]";
  399.   my $count = $1;
  400.   my $off = $_[0] + 2 + length $count;
  401.   my (@res, $res);
  402.   while ($count and length $string > $off) {
  403.     ($res,$off) = thawScalar($off);
  404.     push(@res,$res);
  405.     --$count;
  406.   }
  407.   confess "Wrong length of data in thawing Array: $count left" if $count;
  408.   (\@res, $off);
  409. }
  410.  
  411. sub freezeHash {
  412.   my @arr = sort keys %{$_[0]};
  413.   $string .= '%' . (2*@arr) . '|';
  414.   for (@arr, @{$_[0]}{@arr}) {
  415.     freezeScalar($_);
  416.   }
  417. }
  418.  
  419. sub thawHash {
  420.   my ($arr, $rest) = &thawArray;
  421.   my %hash;
  422.   my $l = @$arr/2;
  423.   foreach (0 .. $l - 1) {
  424.     $hash{$arr->[$_]} = $arr->[$l + $_];
  425.   }
  426.   (\%hash,$rest);
  427. }
  428.  
  429. # Second optional argument: ignore the package
  430. # Third optional one: do not check for duplicates on outer level
  431.  
  432. sub freezeScalar {
  433.   $string .= '_', return unless defined $_[0];
  434.   return &freezeString unless ref $_[0];
  435.   my $ref = ref $_[0];
  436.   my $str;
  437.   if ($_[1] and $ref) {            # Similar to getref()
  438.     if (defined &overload::StrVal) {
  439.       $str = overload::StrVal($_[0]);
  440.     } else {
  441.       $str = "$_[0]";
  442.     }
  443.     $ref = $1 if $str =~ /=(\w+)/;
  444.   } else {
  445.     $str = "$_[0]";
  446.   }
  447.   # Die if a) repeated prohibited, b) met, c) not explicitely requested to ingore.
  448.   confess "Repeated reference met when prohibited"
  449.     if $norepeated && !$_[2] && defined $count{$str};
  450.   if ($secondpass and !$_[2]) {
  451.     $string .= "<$address{$str}|", return
  452.       if defined $count{$str} and $count{$str} > 1;
  453.   } elsif (!$_[2]) {
  454.     # $count{$str} is defined if we have seen it on this pass.
  455.     $address{$str} = @multiple, push(@multiple, $_[0])
  456.       if defined $count{$str} and not exists $address{$str};
  457.     # This is for debugging and shortening thrown-away output (also
  458.     # internal data in arrays and hashes is not duplicated).
  459.     $string .= "<$address{$str}|", ++$count{$str}, return
  460.       if defined $count{$str};
  461.     ++$count{$str};
  462.   }
  463.   return &freezeArray if $ref eq 'ARRAY';
  464.   return &freezeHash if $ref eq 'HASH';
  465.   return &freezeREx if $ref eq 'Regexp' and not defined ${$_[0]};
  466.   $string .= "*", return &freezeString
  467.     if $ref eq 'GLOB' and !$safe;
  468.   $string .= "&", return &freezeString
  469.     if $ref eq 'CODE' and !$safe;
  470.   $string .= '\\', return &freezeScalar( $ {shift()} )
  471.     if $ref eq 'REF' or $ref eq 'SCALAR';
  472.   if ($noCache and (($ref eq 'CODE') or $ref eq 'GLOB')) {
  473.     confess "CODE and GLOB references prohibited now";
  474.   }
  475.   if ($safe and (($ref eq 'CODE') or $ref eq 'GLOB')) {
  476.     $unsafe = 1;
  477.     $saved{$str} = $_[0] unless defined $saved{$str};
  478.     $string .= "?";
  479.     return &freezeString;
  480.   }
  481.   $string .= '>';
  482.   local $norepeated = $norepeated;
  483.   local $noCache = $noCache;
  484.   freezePackage(ref $_[0]);
  485.   $_[0]->Freeze($cooky);
  486. }
  487.  
  488. sub freezePackage {
  489.   my $packageid = $seen_packages{$_[0]};
  490.   if (defined $packageid) {
  491.     $string .= ')';
  492.     &freezeNumber( $packageid );
  493.   } else {
  494.     $string .= '>';
  495.     &freezeNumber( $seen_packages );
  496.     &freezeScalar( $_[0] );
  497.     $seen_packages{ $_[0] } = $seen_packages++;
  498.   }
  499. }
  500.  
  501. sub thawPackage {        # First argument: offset
  502.   my $key = substr($string,$_[0],1);
  503.   my ($get, $rest, $id);
  504.   ($id, $rest) = &thawNumber($_[0] + 1);
  505.   if ($key eq ')') {
  506.     $get = $seen_packages{$id};
  507.   } else {
  508.     ($get, $rest) = &thawString($rest);
  509.     $seen_packages{$id} = $get;
  510.   }
  511.   ($get, $rest);
  512. }
  513.  
  514. # First argument: offset; Optional other: index in the @uninit array
  515.  
  516. sub thawScalar {
  517.   my $key = substr($string,$_[0],1);
  518.   if ($key eq "\$") {&thawString}
  519.   elsif ($key eq '@') {&thawArray}
  520.   elsif ($key eq '%') {&thawHash}
  521.   elsif ($key eq '/') {&thawREx}
  522.   elsif ($key eq '\\') {
  523.     my ($out,$rest) = &thawScalar( $_[0]+1 ) ;
  524.     (\$out,$rest);
  525.   }
  526.   elsif ($key eq '_') { (undef, $_[0]+1) }
  527.   elsif ($key eq '&') {confess "Do not know how to thaw CODE"}
  528.   elsif ($key eq '*') {confess "Do not know how to thaw GLOB"}
  529.   elsif ($key eq '?') {
  530.     my ($address,$rest) = &thawScalar( $_[0]+1 ) ;
  531.     confess "The saved data accessed in unprotected thaw" unless $unsafe;
  532.     confess "The saved data disappeared somewhere"
  533.       unless defined $saved{$address};
  534.     ($saved{$address},$rest);
  535.   } elsif ($key eq '<') {
  536.     confess "Repeated data prohibited at this moment" unless $uninitOK;
  537.     my ($off,$end) = &thawNumber ($_[0]+1);
  538.     ($uninit[$off],$end);
  539.   } elsif ($key eq '>' or $key eq '{' or $key eq '}') {
  540.     my ($package,$rest) = &thawPackage( $_[0]+1 );
  541.     my $cooky = bless \$rest, 'FreezeThaw::TCooky';
  542.     local $uninitOK = $uninitOK;
  543.     local $unsafe = $unsafe;
  544.     if ($key eq '{') {
  545.       my $res = $package->Allocate($cooky);
  546.       ($res, $rest);
  547.     } elsif ($key eq '}') {
  548.       warn "Here it is undef!" unless defined $_[1];
  549.       $package->Instantiate($uninit[$_[1]],$cooky);
  550.       (undef, $rest);
  551.     } else {
  552.       ($package->Thaw($cooky),$rest);
  553.     }
  554.   } else {
  555.     confess "Do not know how to thaw data with code `$key'";
  556.   }
  557. }
  558.  
  559. sub freezeEmpty {        # Takes a type, freezes ref to empty object
  560.   my $e = $Empty{ref $_[0]};
  561.   if (ref $e) {
  562.     my $cache = &$e;
  563.     freezeScalar $cache;
  564.     $cache;
  565.   } elsif ($e) {
  566.     my $cache = shift;
  567.     freezeScalar($cache,1,1);    # Atomic
  568.     $cache;
  569.   } else {
  570.     $string .= "{";
  571.     freezePackage ref $_[0];
  572.     $_[0]->FreezeEmpty($cooky);
  573.   }
  574. }
  575.  
  576. sub freeze {
  577.   local @multiple;
  578.   local %seen_packages;
  579.   local $seen_packages = 0;
  580.   local %seen_packages;
  581. #  local @seentypes;
  582.   local %count;
  583.   local %address;
  584.   local $string = 'FrT;';
  585.   local $unsafe;
  586.   local $noCache;
  587.   local $cooky = bless \$cooky, 'FreezeThaw::FCooky'; # Just something fake
  588.   local $secondpass;
  589.   freezeScalar(\@_);
  590.   if (@multiple) {
  591.     # Now repeated structures are enumerated with order of *second* time
  592.     # they appear in the what we freeze.
  593.     # What we want is to have them enumerated with respect to the first time
  594. ####    $string = '';        # Start again
  595. ####    @multiple = ();
  596. ####    %address = ();
  597. ####    for (keys %count) {
  598. ####      $count{$_} = undef if $count{$_} <= 1; # As at start
  599. ####      $count{$_} = 0 if $count{$_}; # As at start
  600. ####    }
  601. ####    $seen_packages = 0;
  602. ####    %seen_packages = ();
  603. ####    freezeScalar(\@_);
  604.     # Now repeated structures are enumerated with order of first time
  605.     # they appear in the what we freeze
  606. ####    my $oldstring = substr $string, 4;
  607.     $string = 'FrT;!'; # Start again
  608.     $seen_packages = 0;
  609.     %seen_packages = ();    # XXXX We reshuffle parts of the
  610.                                 # string, so the order of packages may
  611.                                 # be wrong...
  612.     freezeNumber($#multiple);
  613.     {
  614.       my @cache;        # Force different values for different
  615.                                 # empty objects.
  616.       foreach (@multiple) {
  617.     push @cache, freezeEmpty $_;
  618.       }
  619.     }
  620. #    for (keys %count) {
  621. #      $count{$_} = undef
  622. #    if !(defined $count{$_}) or $count{$_} <= 1; # As at start
  623. #    }
  624.     # $string .= '@' . @multiple . '|';
  625.     $secondpass = 1;
  626.     for (@multiple) {
  627.       freezeScalar($_,0,1,1), next if $Empty{ref $_};
  628.       $string .= "}";
  629.       freezePackage ref $_;
  630.       $_->FreezeInstance($cooky);
  631.     }
  632. ####    $string .= $oldstring;
  633.     freezeScalar(\@_);
  634.   }
  635.   return "FrT;?\$" . length($lock) . "|" . $lock . substr $string, 4
  636.     if $unsafe;
  637.   $string;
  638. }
  639.  
  640. sub safeFreeze {
  641.   local $safe = 1;
  642.   &freeze;
  643. }
  644.  
  645. sub copyContents {  # Given two references, copies contents of the
  646.                     # second one to the first one, provided they have
  647.             # the same basic type. The package is copied too.
  648.   my($first,$second) = @_;
  649.   my $ref = getref $second;
  650.   if ($ref eq 'SCALAR' or $ref eq 'REF') {
  651.     $$first = $$second;
  652.   } elsif ($ref eq 'ARRAY') {
  653.     @$first = @$second;
  654.   } elsif ($ref eq 'HASH') {
  655.     %$first = %$second;
  656.   } else {
  657.     croak "Don't know how to copyContents of type `$ref'";
  658.   }
  659.   if (ref $second ne ref $first) { # Rebless
  660.     # SvAMAGIC() is a property of a reference, not of a referent!
  661.     # Thus we cannot use $first here if $second was overloaded...
  662.     bless $_[0], ref $second;
  663.   }
  664.   $first;
  665. }
  666.  
  667. sub thaw {
  668.   confess "thaw requires one argument" unless @_ ==1;
  669.   local $string = shift;
  670.   local %seen_packages;
  671.   my $initoff = 0;
  672.   #print STDERR "Thawing `$string'", substr ($string, 0, 4), "\n";
  673.   if (substr($string, 0, 4) ne 'FrT;') {
  674.     warn "Signature not present, continuing anyway" if $^W;
  675.   } else {
  676.     $initoff = 4;
  677.   }
  678.   local $unsafe = $initoff + (substr($string, $initoff, 1) eq "?" ? 1 : 0);
  679.   if ($unsafe != $initoff) {
  680.     my $key;
  681.     ($key,$unsafe) = thawScalar($unsafe);
  682.     confess "The lock in frozen data does not match the key"
  683.       unless $key eq $lock;
  684.   }
  685.   local @multiple;
  686.   local $uninitOK = 1;        # The methods can change it.
  687.   my $repeated = substr($string,$unsafe,1) eq '!' ? 1 : 0;
  688.   my ($res, $off);
  689.   if ($repeated) {
  690.     ($res, $off) = thawNumber($repeated + $unsafe);
  691.   } else {
  692.     ($res, $off) = thawScalar($repeated + $unsafe);
  693.   }
  694.   my $cooky = bless \$off, 'FreezeThaw::TCooky';
  695.   if ($repeated) {
  696.     local @uninit;
  697.     my $lst = $res;
  698.     foreach (0..$lst) {
  699.       ($res, $off) = thawScalar($off, $_);
  700.       push(@uninit, $res);
  701.     }
  702.     my @init;
  703.     foreach (0..$lst) {
  704.       ($res, $off) = thawScalar($off, $_);
  705.       push(@init, $res);
  706.     }
  707.     #($init, $off)  = thawScalar($off);
  708.     #print "Instantiating...\n";
  709.     #my $ref;
  710.     for (0..$#uninit) {
  711.       copyContents $uninit[$_], $init[$_] if ref $init[$_];
  712.     }
  713.     ($res, $off) = thawScalar($off);
  714.   }
  715.   croak "Extra elements in frozen structure: `" . substr($string,$off) . "'"
  716.     if $off != length $string;
  717.   return @$res;
  718. }
  719.  
  720. sub cmpStr {
  721.   confess "Compare requires two arguments" unless @_ == 2;
  722.   freeze(shift) cmp freeze(shift);
  723. }
  724.  
  725. sub cmpStrHard {
  726.   confess "Compare requires two arguments" unless @_ == 2;
  727.   local @multiple;
  728. #  local @seentypes;
  729.   local %count;
  730.   local %address;
  731.   local $string = 'FrT;';
  732.   local $unsafe;
  733.   local $noCache;
  734.   local $cooky = bless \$cooky, 'FreezeThaw::FCooky'; # Just something fake
  735.   freezeScalar($_[0]);
  736.   my %cnt1 = %count;
  737.   freezeScalar($_[1]);
  738.   my %cnt2 = %count;
  739.   %count = ();
  740.   # Now all the caches are filled, delete the entries for guys which
  741.   # are in one argument only.
  742.   my ($elt, $val);
  743.   while (($elt, $val) = each %cnt1) {
  744.     $count{$elt}++ if $cnt2{$elt} > $cnt1{$elt};
  745.   }
  746.   $string = '';
  747.   freezeScalar($_[0]);
  748.   my $str1 = $string;
  749.   $string = '';
  750.   freezeScalar($_[1]);
  751.   $str1 cmp $string;
  752. }
  753.  
  754. #   local $string = freeze(shift,shift);
  755. #   local $uninitOK = 1;
  756. #   #print "$string\n";
  757. #   my $off = 7;            # Hardwired offset after @2|
  758. #   if (substr($string,4,1) eq '!') {
  759. #     $off = 5;            # Hardwired offset after !
  760. #     my ($uninit, $len);
  761. #     ($len,$off) = thawScalar $off;
  762. #     local @uninit;
  763. #     foreach (0..$len) {
  764. #       ($uninit,$off) = thawScalar $off, $_;
  765. #     }
  766. #     $off += 3;            # Hardwired offset after @2|
  767. #   }
  768. #   croak "Unknown format of frozen array: " . substr($string,$off-3)
  769. #     unless substr($string,$off-3,1) eq '@';
  770. #   my ($first,$off2) = thawScalar $off;
  771. #   my $off3;
  772. #   ($first,$off3) = thawScalar $off2;
  773. #   substr($string, $off, $off2-$off) cmp substr($string,$off2,$off3-$off2);
  774. # }
  775.  
  776. sub FreezeThaw::FCooky::FreezeScalar {
  777.   shift;
  778.   &freezeScalar;
  779. }
  780.  
  781. sub FreezeThaw::FCooky::isSafe {
  782.   $safe || $noCache;
  783. }
  784.  
  785. sub FreezeThaw::FCooky::makeSafe {
  786.   $noCache = 1;
  787. }
  788.  
  789. sub FreezeThaw::FCooky::repeatedOK {
  790.   !$norepeated;
  791. }
  792.  
  793. sub FreezeThaw::FCooky::noRepeated {
  794.   $norepeated = 1;
  795. }
  796.  
  797. sub FreezeThaw::TCooky::repeatedOK {
  798.   $uninitOK;
  799. }
  800.  
  801. sub FreezeThaw::TCooky::noRepeated {
  802.   undef $uninitOK;
  803. }
  804.  
  805. sub FreezeThaw::TCooky::isSafe {
  806.   !$unsafe;
  807. }
  808.  
  809. sub FreezeThaw::TCooky::makeSafe {
  810.   undef $unsafe;
  811. }
  812.  
  813. sub FreezeThaw::TCooky::ThawScalar {
  814.   my $self = shift;
  815.   my ($res,$off) = &thawScalar($$self);
  816.   $$self = $off;
  817.   $res;
  818. }
  819.  
  820. sub UNIVERSAL::Freeze {
  821.   my ($obj, $cooky) = (shift, shift);
  822.   $cooky->FreezeScalar($obj,1,1);
  823. }
  824.  
  825. sub UNIVERSAL::Thaw {
  826.   my ($package, $cooky) = (shift, shift);
  827.   my $obj = $cooky->ThawScalar;
  828.   bless $obj, $package;
  829. }
  830.  
  831. sub UNIVERSAL::FreezeInstance {
  832.   my($obj,$cooky) = @_;
  833.   return if (ref $obj and ref $obj eq 'Regexp' and not defined $$obj); # Regexp
  834.   $obj->Freeze($cooky);
  835. }
  836.  
  837. sub UNIVERSAL::Instantiate {
  838.   my($package,$pre,$cooky) = @_;
  839.   return if $package eq 'Regexp';
  840.   my $obj = $package->Thaw($cooky);
  841.   # SvAMAGIC() is a property of a reference, not of a referent!
  842.   # Thus we cannot use $pre here if $obj was overloaded...
  843.   copyContents $_[1], $obj;
  844. }
  845.  
  846. sub UNIVERSAL::Allocate {
  847.   my($package,$cooky) = @_;
  848.   $cooky->ThawScalar;
  849. }
  850.  
  851. sub UNIVERSAL::FreezeEmpty {
  852.   my $obj = shift;
  853.   my $type = getref $obj;
  854.   my $e = $Empty{$type};
  855.   if (ref $e) {
  856.     my $ref = &$e;
  857.     freezeScalar $ref;
  858.     $ref;            # Put into cache.
  859.   } elsif ($e) {
  860.     freezeScalar($obj,1,1);    # Atomic
  861.     undef;
  862.   } elsif (defined $e and not defined $$obj) {    # Regexp
  863.     freezeREx($obj);
  864.     undef;
  865.   } else {
  866.     die "Do not know how to FreezeEmpty $type";
  867.   }
  868. }
  869.  
  870. 1;
  871.